# -*- coding: utf-8 -*-
"""
乒乓球培训管理系统 - 文件上传工具

处理文件上传、验证和存储
"""

import os
import uuid
from datetime import datetime
from werkzeug.utils import secure_filename
from PIL import Image
import io


class FileUploadError(Exception):
    """文件上传异常"""
    pass


def allowed_file(filename, allowed_extensions):
    """
    检查文件扩展名是否允许

    Args:
        filename (str): 文件名
        allowed_extensions (set): 允许的扩展名集合

    Returns:
        bool: 是否允许
    """
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in allowed_extensions


def generate_unique_filename(filename):
    """
    生成唯一文件名

    Args:
        filename (str): 原始文件名

    Returns:
        str: 唯一文件名
    """
    ext = filename.rsplit('.', 1)[1].lower() if '.' in filename else ''
    unique_name = f"{uuid.uuid4().hex}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
    return f"{unique_name}.{ext}" if ext else unique_name


def save_uploaded_file(file, upload_type='general', max_size_mb=5):
    """
    保存上传的文件

    Args:
        file: 文件对象
        upload_type (str): 上传类型（coaches, students, general等）
        max_size_mb (int): 最大文件大小（MB）

    Returns:
        str: 文件URL路径，失败返回None
    """
    try:
        if not file or not file.filename:
            return None

        # 检查文件大小
        file.seek(0, os.SEEK_END)
        file_size = file.tell()
        file.seek(0)

        if file_size > max_size_mb * 1024 * 1024:
            raise FileUploadError(f'文件大小不能超过 {max_size_mb}MB')

        # 检查文件类型
        allowed_extensions = {'png', 'jpg', 'jpeg', 'gif'}
        if not allowed_file(file.filename, allowed_extensions):
            raise FileUploadError('只允许上传图片文件（PNG、JPG、JPEG、GIF）')

        # 生成文件名和路径
        filename = generate_unique_filename(file.filename)
        upload_dir = os.path.join('static', 'uploads', upload_type)

        # 确保上传目录存在
        full_upload_dir = os.path.join(os.getcwd(), 'app', upload_dir)
        os.makedirs(full_upload_dir, exist_ok=True)

        # 保存文件路径
        file_path = os.path.join(full_upload_dir, filename)

        # 如果是图片，进行压缩处理
        if file.filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif')):
            try:
                # 打开图片
                image = Image.open(file.stream)

                # 转换RGBA到RGB（处理PNG透明背景）
                if image.mode in ('RGBA', 'LA'):
                    background = Image.new('RGB', image.size, (255, 255, 255))
                    if image.mode == 'RGBA':
                        background.paste(image, mask=image.split()[-1])
                    else:
                        background.paste(image, mask=image.split()[-1])
                    image = background

                # 限制图片尺寸（最大800x800）
                max_size = (800, 800)
                image.thumbnail(max_size, Image.Resampling.LANCZOS)

                # 保存压缩后的图片
                image.save(file_path, 'JPEG', quality=85, optimize=True)

            except Exception as e:
                # 如果图片处理失败，直接保存原文件
                file.seek(0)
                file.save(file_path)
        else:
            # 非图片文件直接保存
            file.save(file_path)

        # 返回相对URL路径
        return f'/{upload_dir.replace(os.sep, "/")}/{filename}'

    except FileUploadError:
        raise
    except Exception as e:
        raise FileUploadError(f'文件上传失败：{str(e)}')


def delete_uploaded_file(file_url):
    """
    删除上传的文件

    Args:
        file_url (str): 文件URL路径

    Returns:
        bool: 是否删除成功
    """
    try:
        if not file_url or not file_url.startswith('/static/uploads/'):
            return False

        # 构建文件的实际路径
        file_path = os.path.join(os.getcwd(), 'app', file_url.lstrip('/'))

        if os.path.exists(file_path):
            os.remove(file_path)
            return True

        return False

    except Exception as e:
        print(f"Error deleting file {file_url}: {str(e)}")
        return False


def get_file_info(file_url):
    """
    获取文件信息

    Args:
        file_url (str): 文件URL路径

    Returns:
        dict: 文件信息字典
    """
    try:
        if not file_url or not file_url.startswith('/static/uploads/'):
            return None

        file_path = os.path.join(os.getcwd(), 'app', file_url.lstrip('/'))

        if not os.path.exists(file_path):
            return None

        stat = os.stat(file_path)

        return {
            'size': stat.st_size,
            'created_at': datetime.fromtimestamp(stat.st_ctime),
            'modified_at': datetime.fromtimestamp(stat.st_mtime),
            'exists': True
        }

    except Exception as e:
        print(f"Error getting file info for {file_url}: {str(e)}")
        return None


def validate_image_file(file, max_size_mb=5, allowed_dimensions=(50, 50, 2000, 2000)):
    """
    验证图片文件

    Args:
        file: 文件对象
        max_size_mb (int): 最大文件大小（MB）
        allowed_dimensions (tuple): 允许的尺寸范围 (min_width, min_height, max_width, max_height)

    Returns:
        tuple: (是否有效, 错误信息)
    """
    try:
        if not file or not file.filename:
            return False, '请选择文件'

        # 检查文件大小
        file.seek(0, os.SEEK_END)
        file_size = file.tell()
        file.seek(0)

        if file_size > max_size_mb * 1024 * 1024:
            return False, f'文件大小不能超过 {max_size_mb}MB'

        # 检查文件扩展名
        allowed_extensions = {'png', 'jpg', 'jpeg', 'gif'}
        if not allowed_file(file.filename, allowed_extensions):
            return False, '只允许上传图片文件（PNG、JPG、JPEG、GIF）'

        # 检查图片尺寸
        try:
            image = Image.open(file.stream)
            width, height = image.size
            file.seek(0)

            min_w, min_h, max_w, max_h = allowed_dimensions
            if width < min_w or height < min_h:
                return False, f'图片尺寸太小，最小为 {min_w}x{min_h} 像素'
            if width > max_w or height > max_h:
                return False, f'图片尺寸太大，最大为 {max_w}x{max_h} 像素'

        except Exception as e:
            return False, '无效的图片文件'

        return True, None

    except Exception as e:
        return False, f'文件验证失败：{str(e)}'